﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using System.Runtime.Serialization;
using Hvac;

namespace Simulator
{
    public partial class SimulatorForm : Form
    {
        ZoneController z;
        Therms therms;

        public SimulatorForm()
        {
            InitializeComponent();

            OnScreenSystem hvacSystem = new OnScreenSystem(cbAC);

            IAirVolumeController avc;
            Zone tsave, t;
            ITemperatureSensor sensor;

            therms = new Therms(73.5);

            sensor = new OnScreenTempSensor(masterTemp);
            avc = new OnScreenDamper(masterDamper, 0);
            tsave = therms.GetThermostat("Master");
            t = new Zone("Master", avc, sensor, tsave.SetPointLow, tsave.SetPointHigh, tsave.SatisfySetting);
            masterThermo.Value = (decimal)t.SetPointHigh;
            masterThermo.Tag = t;
            masterThermo.ValueChanged += new System.EventHandler(this.thermoHigh_ValueChanged);
            masterThermoLow.Value = (decimal)t.SetPointLow;
            masterThermoLow.Tag = t;
            masterThermoLow.ValueChanged += new EventHandler(thermoLow_ValueChanged);
            masterPriority.Tag = t;
            masterPriority.SelectedIndex = (int)t.SatisfySetting;
            masterPriority.SelectedIndexChanged += new EventHandler(priority_SelectedIndexChanged);
            hvacSystem.AddZone(t);

            sensor = new OnScreenTempSensor(bonusTemp);
            avc = new OnScreenDamper(bonusDamper, 1);
            tsave = therms.GetThermostat("Bonus");
            t = new Zone("Bonus", avc, sensor, tsave.SetPointLow, tsave.SetPointHigh, tsave.SatisfySetting);
            bonusThermo.Value = (decimal)t.SetPointHigh;
            bonusThermo.Tag = t;
            bonusThermo.ValueChanged += new System.EventHandler(this.thermoHigh_ValueChanged);
            bonusThermoLow.Value = (decimal)t.SetPointLow;
            bonusThermoLow.Tag = t;
            bonusThermoLow.ValueChanged += new EventHandler(thermoLow_ValueChanged);
            bonusPriority.Tag = t;
            bonusPriority.SelectedIndex = (int)t.SatisfySetting;
            bonusPriority.SelectedIndexChanged += new EventHandler(priority_SelectedIndexChanged);
            hvacSystem.AddZone(t);

            sensor = new OnScreenTempSensor(hunterTemp);
            avc = new OnScreenDamper(hunterDamper, 2, 700, 1400);
            tsave = therms.GetThermostat("Hunter");
            t = new Zone("Hunter", avc, sensor, tsave.SetPointLow, tsave.SetPointHigh, tsave.SatisfySetting);
            hunterThermo.Value = (decimal)t.SetPointHigh;
            hunterThermo.Tag = t;
            hunterThermo.ValueChanged += new System.EventHandler(this.thermoHigh_ValueChanged);
            hunterThermoLow.Value = (decimal)t.SetPointLow;
            hunterThermoLow.Tag = t;
            hunterThermoLow.ValueChanged += new EventHandler(thermoLow_ValueChanged);
            hunterPriority.Tag = t;
            hunterPriority.SelectedIndex = (int)t.SatisfySetting;
            hunterPriority.SelectedIndexChanged += new EventHandler(priority_SelectedIndexChanged);
            hvacSystem.AddZone(t);

            sensor = new OnScreenTempSensor(carolineTemp);
            avc = new OnScreenDamper(carolineDamper, 3);
            tsave = therms.GetThermostat("Caroline");
            t = new Zone("Caroline", avc, sensor, tsave.SetPointLow, tsave.SetPointHigh, tsave.SatisfySetting);
            carolineThermo.Value = (decimal)t.SetPointHigh;
            carolineThermo.Tag = t;
            carolineThermo.ValueChanged += new System.EventHandler(this.thermoHigh_ValueChanged);
            carolineThermoLow.Value = (decimal)t.SetPointLow;
            carolineThermoLow.Tag = t;
            carolineThermoLow.ValueChanged += new EventHandler(thermoLow_ValueChanged);
            carolinePriority.Tag = t;
            carolinePriority.SelectedIndex = (int)t.SatisfySetting;
            carolinePriority.SelectedIndexChanged += new EventHandler(priority_SelectedIndexChanged);
            hvacSystem.AddZone(t);

            z = new ZoneController(hvacSystem);
        }

        void priority_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox control = (ComboBox)sender;
            Zone t = (Zone)control.Tag;
            t.SatisfySetting = (SatisfyOption)control.SelectedIndex;
            therms.PutValue(t.Name, t);
            therms.Save();
        }

        void thermoLow_ValueChanged(object sender, EventArgs e)
        {
            NumericUpDown control = (NumericUpDown)sender;
            Zone t = (Zone)control.Tag;
            t.SetPointLow = (double)control.Value;
            therms.PutValue(t.Name, t);
            therms.Save();
        }

        private void thermoHigh_ValueChanged(object sender, EventArgs e)
        {
            NumericUpDown control = (NumericUpDown)sender;
            Zone t = (Zone)control.Tag;
            t.SetPointHigh = (double)control.Value;
            therms.PutValue(t.Name, t);
            therms.Save();
        }

        private void cbEnableController_CheckedChanged(object sender, EventArgs e)
        {
            z.Enabled = cbEnableController.Checked;
        }
    }

    /// <summary>
    /// This class persists the current thermostat settings
    /// </summary>
    [DataContract]
    class Therms
    {
        class sensor : ITemperatureSensor
        {
            public double Temperature { get { return 0; } }
            public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;
        }

        [DataMember]
        IDictionary<string, Zone> therms = null;

        private readonly string _defaultValueName = "_defaultValue";

        public Therms(double defaultValue)
        {
            Load();

            if (therms == null)
            {
                therms = new Dictionary<string, Zone>();
            }

            if (!Exists(_defaultValueName))
                PutValue(_defaultValueName, new Zone("_defaultValue", null, new sensor(), 60, 73.5, SatisfyOption.MustSatisfy));
        }

        public bool Exists(string name)
        {
            return therms.ContainsKey(name);
        }

        public Zone GetThermostat(string name)
        {
            if (Exists(name))
                return therms[name];
            else
                return therms[_defaultValueName];
        }

        public void PutValue(string name, Zone value)
        {
            therms[name] = value;
        }

        const string fileName = "therms.xml";

        public void Save()
        {
            FileStream writer = new FileStream(fileName, FileMode.Create);
            DataContractSerializer ser = new DataContractSerializer(typeof(IDictionary<string, Zone>));
            ser.WriteObject(writer, therms);
            writer.Close();
        }

        public void Load()
        {
            if (File.Exists(fileName))
            {
                FileStream fs = new FileStream(fileName, FileMode.Open);
                XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
                DataContractSerializer ser = new DataContractSerializer(typeof(IDictionary<string, Zone>));
                therms = (IDictionary<string, Zone>)ser.ReadObject(reader, true);
                reader.Close();
                fs.Close();
            }
        }
    }

    class OnScreenSystem : IHvacSystem
    {
        IHvacController hc;
        List<Zone> zones;

        public OnScreenSystem(CheckBox cbAC)
        {
            hc = new OnScreenHVACController(cbAC);
            zones = new List<Zone>();
        }

        public void AddZone(Zone z)
        {
            zones.Add(z);
        }

        #region IHvacSystem Members

        public IHvacController HvacController
        {
            get { return hc; }
        }

        public IEnumerable<Zone> Zones
        {
            get { return zones; }
        }

        #endregion
    }

    class OnScreenHVACController : IHvacController
    {
        [Flags]
        enum Pins
        {
            Enable = 1,
            Heat = 2,
            Cool = 4,
            Fan = 8
        }

        private bool isRunning = false;
        private int mode;
        private CheckBox _box;
        private double lastDemand;
        private Pins servoPins;
        DateTime timeOfLastModeChange;

        public OnScreenHVACController(CheckBox box)
        {
            mode = 1;
            _box = box;
        }

        public event EventHandler<HvacChangedEventArgs> HvacChanged;

        protected virtual void OnHVACChanged(HvacChangedEventArgs e)
        {
            if (HvacChanged != null)
                HvacChanged(this, e);
        }

        #region IHVACController Members

        public bool IsRunning
        {
            get { return isRunning; }
        }

        public void Demand(double factor)
        {
            Console.WriteLine("AC demand is {0}", factor);
            if (mode == 0)
                factor = 0;

            if (factor != lastDemand)
            {
                lastDemand = factor;
                bool wasRunning = isRunning;

                System.Diagnostics.Debug.WriteLine(string.Format("AC demand is {0}", factor));

                if (factor == 0 && isRunning)
                {
                    isRunning = false;
                }
                else
                    isRunning = true;

                if (isRunning && mode != 0)
                {
                    if (mode == -1)
                    {
                        servoPins |= Pins.Cool;
                        servoPins ^= servoPins & Pins.Heat;
                    }
                    else
                    {
                        servoPins ^= servoPins & Pins.Cool;
                        servoPins |= Pins.Heat;
                    }
                }
                else
                {
                    servoPins ^= servoPins & (Pins.Cool | Pins.Heat);
                }

                string cmd = string.Format("#{0}:{1}", 2, (int)servoPins);
                Console.WriteLine(cmd);

                if (wasRunning != isRunning)
                {
                    DateTime currentTime = DateTime.Now;
                    TimeSpan timeInLastMode = currentTime - timeOfLastModeChange;
                    timeOfLastModeChange = currentTime;
                    _box.Text = string.Format("{0} {1} at {2} ({3} in last mode)", mode == -1 ? "Cool" : "Heat", isRunning ? "on" : "off", timeOfLastModeChange, FormatTimeSpan(timeInLastMode));
                    OnHVACChanged(new HvacChangedEventArgs(isRunning, mode));
                }
            }

            _box.Checked = isRunning;
        }

        string FormatTimeSpan(TimeSpan time)
        {
            string timeString;

            if (time.TotalSeconds <= 59)
                timeString = string.Format("{0} secs", time.Seconds);
            else if (time.TotalMinutes <= 90)
                timeString = string.Format("{0} mins", time.Minutes + time.Hours * 60);
            else if (time.TotalDays > 0)
                timeString = string.Format("{0}.{1}:{2}", time.Days, time.Hours.ToString("00"), time.Minutes.ToString("00"));
            else
                timeString = string.Format("{0}:{1}", time.Hours.ToString("00"), time.Minutes.ToString("00"));

            return timeString;
        }

        public int Mode
        {
            get
            {
                return mode;
            }
            set
            {
                mode = value;
            }
        }

        private bool enabled;

        public bool Enabled
        {
            get { return (servoPins & Pins.Enable) == Pins.Enable; }
            set
            {
                if (value != enabled)
                {
                    enabled = value;

                    servoPins ^= Pins.Enable;

                    //string cmd = string.Format("#{0}{1}", 16, enabled ? "H" : "L");
                    string cmd = string.Format("#{0}:{1}", 2, (int)servoPins);
                    Console.WriteLine(cmd);
                }
            }
        }

        public FanMode FanStatus
        {
            get
            {
                return FanMode.Auto;
            }
            set
            {
                ;
            }
        }
        #endregion
    }

    class OnScreenDamper : IAirVolumeController
    {
        Label lbControl;
        int port;

        public OnScreenDamper(Label lbControl, int port, int openFrequency, int closedFrequency) :
            this(lbControl, port)
        {
            OpenFrequency = openFrequency;
            ClosedFrequency = closedFrequency;
            Range = OpenFrequency - ClosedFrequency;
        }

        public OnScreenDamper(Label lbControl, int port)
        {
            this.lbControl = lbControl;
            this.port = port;

            Range = OpenFrequency - ClosedFrequency;

            LastPosition = -1;
            SetVolume(1);
        }

        #region IDamper Members

        public void SetVolume(double position)
        {
            lbControl.Text = position.ToString("P0");

            if (position != LastPosition)
            {
                //LastPosition = position;

                // Calculate frequency
                int frequency = (int)(Range * position) + ClosedFrequency;

                string cmd = string.Format("#{0} P{1} S1000", port, frequency);
                System.Diagnostics.Debug.WriteLine(cmd);
            }
        }

        #endregion

        int OpenFrequency = 2000;
        int ClosedFrequency = 1000;
        int Range;
        double LastPosition;
    }

    class OnScreenTempSensor : ITemperatureSensor
    {
        NumericUpDown control;
        double oldTemp;

        public OnScreenTempSensor(NumericUpDown onScreenControl)
        {
            control = onScreenControl;
            control.ValueChanged += new EventHandler(control_ValueChanged);
        }

        void control_ValueChanged(object sender, EventArgs e)
        {
            double temp = (double)control.Value;
            OnTemperatureChanged(new TemperatureChangedEventArgs(oldTemp, temp));
            oldTemp = temp;
        }

        #region ITemperatureSensor Members

        public double Temperature
        {
            get
            {
                double temp = (double)control.Value;
                if (temp != oldTemp)
                {
                    OnTemperatureChanged(new TemperatureChangedEventArgs(oldTemp, temp));
                    oldTemp = temp;
                }

                return oldTemp;
            }
        }

        public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;

        #endregion

        protected virtual void OnTemperatureChanged(TemperatureChangedEventArgs e)
        {
            if (TemperatureChanged != null)
                TemperatureChanged(this, e);
        }
    }
}
